/*
 * $Id: MemoryPool.cpp 50 2009-02-10 05:48:05Z hyunghwan.chung $
 *
   Copyright 2005-2009 Chung, Hyung-Hwan.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an  BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */

#include <xp/bas/MemoryPool.hpp>

XP_BEGIN_NAMESPACE2 (xp, bas)


MemoryPool::MemoryPool (xp_size_t datum_size, xp_size_t block_size)
{
	if (datum_size > 0 && 
	    datum_size < XP_SIZEOF(void*)) datum_size = XP_SIZEOF(void*);

	this->mp_blocks  = XP_NULL;
	this->free_list  = XP_NULL;
	this->datum_size = datum_size;
	this->block_size = block_size;
#ifdef XP_DEBUG_VERBOSE
	this->navail = 0;
	this->nalloc = 0;
#endif
}
MemoryPool::~MemoryPool ()
{
	this->dispose ();
}

void* MemoryPool::allocate ()
{
	if (this->datum_size <= 0 ||
	    this->block_size <= 0) return XP_NULL;

	void* ptr = this->free_list;
	if (ptr == XP_NULL) 
	{
		this->add_block ();
		ptr = this->free_list;
	}
	this->free_list = this->free_list->next;
#ifdef XP_DEBUG_VERBOSE
	this->navail--;
#endif
	return ptr;
}

void MemoryPool::dispose (void* ptr)
{
	((Chain*)ptr)->next = this->free_list;
	this->free_list = (Chain*)ptr;
#ifdef XP_DEBUG_VERBOSE
	this->navail++;
#endif
}

void MemoryPool::dispose ()
{
	Block* block = this->mp_blocks;
	while (block != XP_NULL) 
	{
		Block* next = block->next;
		::delete[] block;
		block = next;
	}

	this->free_list = XP_NULL;
	this->mp_blocks = XP_NULL;
#ifdef XP_DEBUG_VERBOSE
	this->navail = 0;
	this->nalloc = 0;
#endif
}

void MemoryPool::add_block ()
{
	XP_ASSERT (this->datum_size > 0 && this->block_size > 0);

	Block* block = (Block*)::new xp_byte_t[
		XP_SIZEOF(Block) + this->block_size * this->datum_size];

	//this->free_list = (Chain*)block->data;
	this->free_list = (Chain*)(block + 1);
	Chain* ptr = this->free_list;
	for (xp_size_t i = 0; i < this->block_size - 1; i++) {
		Chain* next = (Chain*)((xp_byte_t*)ptr + this->datum_size);
		ptr->next = next;
		ptr = next;
	}
	ptr->next = XP_NULL;

	block->next  = this->mp_blocks;
	this->mp_blocks = block;
#ifdef XP_DEBUG_VERBOSE
	this->navail += this->block_size;
	this->nalloc += this->block_size;
#endif
}

int MemoryPool::swap (MemoryPool& mpool)
{
	// this function is sensitive to member variables changes.
	// whenever you add new member variables, this function require
	// relevant changes.

	if (this != &mpool)
	{
	#ifdef XP_DEBUG_VERBOSE
		xp_size_t org_nalloc = this->nalloc;
		xp_size_t org_navail = this->navail;

		this->nalloc = mpool.nalloc;
		this->navail = mpool.navail;

		mpool.nalloc = org_nalloc;
		mpool.navail = org_navail;
	#endif

		Block* org_mp_blocks = this->mp_blocks;
		Chain* org_free_list = this->free_list;
		xp_size_t org_datum_size = this->datum_size;
		xp_size_t org_block_size = this->block_size;

		this->mp_blocks = mpool.mp_blocks;
		this->free_list = mpool.free_list;
		this->datum_size = mpool.datum_size;
		this->block_size = mpool.block_size;

		mpool.mp_blocks = org_mp_blocks;
		mpool.free_list = org_free_list;
		mpool.datum_size = org_datum_size;
		mpool.block_size = org_block_size;
	}

	return 0;
}

XP_END_NAMESPACE2 (xp, bas)
